# -*- coding: utf-8 -*-
import json
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from base64 import decodebytes, encodebytes

__author__ = "xuanpeng"


class APICertifier(object):
    """
    接口访问验证
    """

    def __init__(self, private_key_path=None, private_key_string=None,
                 public_key_path=None, public_key_string=None):
        self._private_key_path = private_key_path
        self._private_key_string = private_key_string
        self._public_key_path = public_key_path
        self._public_key_string = public_key_string
        self._private_key = None
        self._public_key = None

    @property
    def private_key(self):
        """
        签名用

        :return:
        """
        if self._private_key:
            return self._private_key

        if self._private_key_path:
            with open(self._private_key_path) as fp:
                self._private_key = RSA.importKey(fp.read())
            return self._private_key

        if self._private_key_string:
            self._private_key = RSA.importKey(self._private_key_string)
            return self._private_key

        raise Exception("私钥没有设置")

    @property
    def public_key(self):
        """
        验证签名用

        :return:
        """
        if self._public_key:
            return self._public_key

        if self._public_key_path:
            with open(self._public_key_path) as fp:
                self._public_key = RSA.importKey(fp.read())
            return self._public_key

        if self._public_key_string:
            self._public_key = RSA.importKey(self._public_key_string)
            return self._public_key

        raise Exception("公钥没有设置")

    def sign_data(self, data):
        data.pop("sign", None)
        # 排序后的字符串
        unsigned_items = self._ordered_data(data)
        unsigned_string = "&".join("{}={}".format(k, v) for k, v in unsigned_items)
        sign = self._sign(unsigned_string)
        return sign

    def verify(self, data, signature):
        """
        验证数据

        :param dict data:
        :param str signature:
        :return:
        :rtype: bool
        """
        # 排序后的字符串
        unsigned_items = self._ordered_data(data)
        message = "&".join(u"{}={}".format(k, v) for k, v in unsigned_items)
        return self._verify(message, signature)

    def _verify(self, raw_content, signature):
        # 开始计算签名
        key = self.public_key
        signer = PKCS1_v1_5.new(key)
        digest = SHA256.new()
        digest.update(raw_content.encode("utf8"))
        if signer.verify(digest, decodebytes(signature.encode("utf8"))):
            return True
        return False

    def _sign(self, unsigned_string):
        """
        通过如下方法调试签名
        方法1
            key = rsa.PrivateKey.load_pkcs1(open(self._app_private_key_path).read())
            sign = rsa.sign(unsigned_string.encode("utf8"), key, "SHA-1")
            # base64 编码，转换为unicode表示并移除回车
            sign = base64.encodebytes(sign).decode("utf8").replace("\n", "")
        方法2
            key = RSA.importKey(open(self._app_private_key_path).read())
            signer = PKCS1_v1_5.new(key)
            signature = signer.sign(SHA.new(unsigned_string.encode("utf8")))
            # base64 编码，转换为unicode表示并移除回车
            sign = base64.encodebytes(signature).decode("utf8").replace("\n", "")
        方法3
            echo "abc" | openssl sha1 -sign alipay.key | openssl base64

        """
        # 开始计算签名
        key = self.private_key
        signer = PKCS1_v1_5.new(key)

        signature = signer.sign(SHA256.new(unsigned_string.encode("utf-8")))
        # base64 编码，转换为unicode表示并移除回车
        sign = encodebytes(signature).decode("utf8").replace("\n", "")
        return sign

    def _ordered_data(self, data):
        """
        将请求数据排序

        :param dict data:
        :return:
        :rtype: dict
        """
        complex_keys = []
        for key, value in data.items():
            if isinstance(value, dict):
                complex_keys.append(key)

            # 将字典类型的数据dump出来
        for key in complex_keys:
            data[key] = json.dumps(data[key], separators=(',', ':'))

        return sorted([(k, v) for k, v in data.items()])


certifier = APICertifier(private_key_path="./encryption/cert/account_private.pem",
                         public_key_path="./encryption/cert/account_public.pem")
